﻿using DotNetty.Buffers;
using Face8280WebSocketServerTest.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Concurrent;
using System.Net;
using System.Net.WebSockets;
using System.Text;

namespace Face8280WebSocketServerTest.Controllers
{
    public class WebSocketController : ControllerBase
    {
        static HashSet<string> SystemHttpHeaders = new HashSet<string>() { "Connection", "Host", "Upgrade", "Sec-WebSocket-Version", "Sec-WebSocket-Key" };

        public static ConcurrentDictionary<string, WebSocket> WebSockets = new ConcurrentDictionary<string, WebSocket>();
        // ConcurrentBag

        [HttpGet("/Webscoket")]
        public async Task Get()
        {
            if (HttpContext.WebSockets.IsWebSocketRequest)
            {
                var request = HttpContext.Request;

                Console.WriteLine("连接已打开，参数字符串：" + request.QueryString.ToString());
                if (request.Headers.Count > 0)
                {
                    var skeys = request.Headers.Keys;
                    foreach (var skey in skeys)
                    {
                        if (!SystemHttpHeaders.Contains(skey))
                            Console.WriteLine($"http头：{skey} = {request.Headers[skey]}");
                    }


                }
                using WebSocket webSocket = await
                                   HttpContext.WebSockets.AcceptWebSocketAsync();
                await Echo(HttpContext, webSocket);
            }
            else
            {
                HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            }
        }

        private async Task Echo(HttpContext context, WebSocket webSocket)
        {
            string id = context.Request.Host.Value;
            try
            {
                if (!WebSockets.ContainsKey(id))
                    WebSockets.TryAdd(id, webSocket);
                var buffer = new byte[1024 * 4];
                IByteBuffer bData = null;
                Keepalive(webSocket);
                WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                var defultLen = 1024 * 1024 * 2;
                var maxLen = defultLen;
                while (!result.CloseStatus.HasValue)
                {

                    if (bData == null)
                        bData = UnpooledByteBufferAllocator.Default.Buffer(defultLen);
                    bData.WriteBytes(buffer, 0, result.Count);
                    if (bData.ReadableBytes > maxLen)
                    {
                        bData.Clear();
                        break;
                    }
                    if (result.EndOfMessage)
                    {
                        Console.WriteLine("收到二进制数据:" + bData.ReadableBytes);
                        var dataLength = bData.ReadInt();
                        var bStr = bData.ReadString(dataLength, Encoding.UTF8);
                        byte[] files = null;
                        if (bData.ReadableBytes != 0)
                        {
                            files = new byte[bData.ReadableBytes];
                            bData.ReadBytes(files);
                        }
                        Console.WriteLine("数据内容:" + bStr);
                        if (files != null)
                        {
                            Console.WriteLine("文件数据长度" + files.Length);
                        }

                        var jobj = JsonConvert.DeserializeObject<JsonRequest>(bStr);
                        // var cmd = jobj.Property("Cmd", StringComparison.OrdinalIgnoreCase);
                        if (jobj != null)
                        {
                            //var cmd = jobj["cmd"]?.ToString();
                            //var buf = GetResult(cmd, jobj, files);
                            //await webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), result.MessageType, result.EndOfMessage, CancellationToken.None);

                            SendCmd(jobj, bStr, webSocket, files);
                        }
                        bData.Clear();
                    }
                    result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                }
                await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
            }
            catch (Exception ex)
            {
                Console.WriteLine("连接发生错误:" + ex.Message);
            }
            WebSockets.Remove(id, out _);
            Console.WriteLine("连接已断开");
        }
        JsonSerializerSettings setting = new JsonSerializerSettings
        {
            ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
        };

        private void Keepalive(WebSocket webSocket)
        {
            Task.Factory.StartNew(() =>
            {
                var data = new
                {
                    msgtype= "PushStatus",
                    workstatus = new
                    {
                        ups= "12.1",
                        voltage= "12.5",
                        Temperature= "45.5",
                        relay=1,
                        keepopen=1,
                        doorSensor=0,
                        lockSensor=1,
                        locked=1,
                        msgPush=1,
                        alarm= "anti"
                    },
                    relays="0101010101010101010101010101010101010101010101010101010101010101"
                };
                var buf = UnpooledByteBufferAllocator.Default.Buffer();
                var dataBuf = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data));
                buf.WriteInt(dataBuf.Length);
                buf.WriteBytes(dataBuf);
                while (true)
                {
                    if (webSocket.State == WebSocketState.Aborted)
                        break;
                    webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
                    Thread.Sleep(30000);
                }
            });
        }


        private async void SendCmd(JsonRequest json, string requestStr, WebSocket webSocket, byte[] files)
        {
            if (json.Cmd == null) return;
            var buf = UnpooledByteBufferAllocator.Default.Buffer();
            var cmd = json.Cmd;
            switch (cmd.ToLower())
            {
                case "getworkparameter":
                    var workParameter = System.IO.File.ReadAllText("WorkParameter.json");
                    var len = Encoding.UTF8.GetByteCount(workParameter);
                    buf.WriteInt(len);
                    buf.WriteString(workParameter, Encoding.UTF8);
                    await webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
                    Console.WriteLine("响应:" + cmd + " ,返回长度:" + len);
                    break;
                case "setworkparameter":
                    System.IO.File.WriteAllText("WorkParameter2.json", requestStr);
                    var resultStr = JsonConvert.SerializeObject(new JsonResponse { Reply = cmd, Result = 200 });
                    buf.WriteInt(Encoding.UTF8.GetByteCount(resultStr));
                    buf.WriteString(resultStr, Encoding.UTF8);
                    await webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
                    break;
                case "getperson":
                    GetPerson(json, webSocket);
                    break;
                case "getpersonlist":
                    await SendGetPersonList(json, webSocket);
                    break;
                case "addperson":
                    var getStatus = "{\"reply\":\"" + cmd + "\",\"result\":200}";
                    buf.WriteInt(Encoding.UTF8.GetByteCount(getStatus));
                    buf.WriteString(getStatus, Encoding.UTF8);
                    webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
                    break;
                case "getrecord":
                    break;
                case "pushsoftware":
                    Pushsoftware(json, webSocket, files);

                    break;
                //case "getStatus":
                //    var getStatus = "{\"reply\":\"" + cmd + "\",\"result\":200}";
                //    buf.WriteInt(Encoding.UTF8.GetByteCount(getStatus));
                //    buf.WriteString(getStatus, Encoding.UTF8);
                //    webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
                //    break;
                default:
                    var sResult = "{\"reply\":\"" + cmd + "\",\"result\":200,\"error\":\"200\"}";
                    buf.WriteInt(Encoding.UTF8.GetByteCount(sResult));
                    buf.WriteString(sResult, Encoding.UTF8);
                    webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
                    break;
            }
        }

        long fileLastIndex = 0;

        public void Pushsoftware(JsonRequest json, WebSocket webSocket, byte[] files)
        {
            if (json.data == null)
            {
                return;
            }
            var ranges = json.data.range.Split('-');
            var index = long.Parse(ranges[0]);
            var last = long.Parse(ranges[1]);
            var fileName = json.version + "Pushsoftware.pkg";
            var buf = UnpooledByteBufferAllocator.Default.Buffer();
            if (index == 0)
            {
                System.IO.File.Delete(fileName);
            }
            else if (fileLastIndex != index)
            {
                var sResult = "{\"reply\":\"" + json.Cmd + "\",\"result\":401,\"error\":\"固件接收错误\"}";
                buf.WriteInt(Encoding.UTF8.GetByteCount(sResult));
                buf.WriteString(sResult, Encoding.UTF8);
                webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
                return;
            }

            using FileStream stream = new System.IO.FileStream(fileName, FileMode.Append, FileAccess.Write);
            stream.Position = index;
            Console.WriteLine("Position=" + stream.Position);
            stream.Write(files, 0, files.Length);
            stream.Flush();
            stream.Close();
            stream.Dispose();
            var ok = "{\"reply\":\"" + json.Cmd + "\",\"result\":200,\"error\":\"固件接收成功\" ,\"range\":\"" + json.data.range + "\"}";
            buf.WriteInt(Encoding.UTF8.GetByteCount(ok));
            buf.WriteString(ok, Encoding.UTF8);
            webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
            fileLastIndex = last;
        }
        public void GetPerson(JsonRequest json, WebSocket webSocket)
        {
            var file = System.IO.File.ReadAllText("PersonList.json");
            // var code = Convert.ToInt32(json["code"]);
            var personList = JsonConvert.DeserializeObject<List<Models.Person>>(file);
            var person = personList.FirstOrDefault(a => a.Code == json.Code);
            (string, byte[]) result;
            person.Reply = json.Cmd;
            person.Result = 200;
            var bFile = System.IO.File.ReadAllBytes($"person{json.Code}.jpg");
            person.FeatureData[0].Len = bFile.Length;
            var sjson = JsonConvert.SerializeObject(person, setting);
            var bJson = Encoding.UTF8.GetBytes(sjson);
            var buf = UnpooledByteBufferAllocator.Default.Buffer();
            int len = bJson.Length;
            buf.WriteInt(len);
            buf.WriteBytes(bJson);
            buf.WriteBytes(bFile);
            webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);
        }



        public async Task SendGetPersonList(JsonRequest json, WebSocket webSocket)
        {
            var file = System.IO.File.ReadAllText("PersonList.json");
            var personList = JsonConvert.DeserializeObject<List<Models.GetPersonModel>>(file);
            Models.GetPersonListResult result = new Models.GetPersonListResult();
            var buf = UnpooledByteBufferAllocator.Default.Buffer();
            if (personList == null || personList.Count == 0)
            {
                await Send(webSocket, result);
                return;
            }
            var list = personList.Skip((json.pageNum - 1) * json.pageSize).Take(json.pageSize).ToList();
            result.Reply = json.Cmd;
            result.Total = personList.Count;
            result.PageNum = json.pageNum;
            result.PageSize = json.pageSize;
            result.Result = 200;
            result.Persons = list;

            var sjson = JsonConvert.SerializeObject(result, setting);
            var bJson = Encoding.UTF8.GetBytes(sjson);
            //var buf = UnpooledByteBufferAllocator.Default.Buffer();
            int len = bJson.Length;
            buf.WriteInt(len);
            buf.WriteBytes(bJson);
            // buf.WriteBytes(bFile);
            webSocket.SendAsync(new ArraySegment<byte>(buf.Array, 0, buf.ReadableBytes), WebSocketMessageType.Binary, true, CancellationToken.None);

        }

        public async Task SendGetRecord(JObject json, WebSocket webSocket)
        {

        }
        private async Task Send(WebSocket webSocket, object oData)
        {
            var sData = JsonConvert.SerializeObject(oData, setting);
            await Send(webSocket, Encoding.UTF8.GetBytes(sData));
        }

        private async Task Send(WebSocket webSocket, object oData, IByteBuffer files)
        {
            var buf = UnpooledByteBufferAllocator.Default.Buffer();
            var sData = JsonConvert.SerializeObject(oData, setting);
            buf.WriteInt(Encoding.UTF8.GetByteCount(sData));
            buf.WriteString(sData, Encoding.UTF8);
            if (files != null && files.ReadableBytes > 0)
                buf.WriteBytes(files);
            await webSocket.SendAsync(new ArraySegment<byte>(buf.Array), WebSocketMessageType.Binary, true, CancellationToken.None);
        }

    }
}
